// apply/call/bind
// curry/compose
// throttle/debounce
// 寄生组合

Function.prototype.myApply = function (context, ...args) {
    let key = new Symbol()
    context[key] = this;
    let res;
    if (Array.isArray(args)) {
        res = context[key](...args)
    } else {
        res = context[key]()
    }
    delete context[key]
    return res;
}

function curry(fn) {
    return function curryFn(...args){
        if(args.length >= fn.length){
            return fn.apply(this, args)
        } else {
            return (...params) => {
                return curryFn.apply(this, [...args, ...params])
            }
        }
    }
}
var a = (a, b, c, d, e) => a + b + c + d + e
let newFn = curry(a)
console.log(newFn(1, 2)(3, 4, 5, 6, 6))


function curry2(...args) {
    let arr = args
    function curryFn(...params){
        arr = [...arr, ...params]
        return curryFn
    }
    curryFn.toString = () => {
        return arr.reduce((pre, next) => pre + next, 0)
    }
    return curryFn
}
console.log(curry2(1)(2)(3).toString())

function compose(...args) {
    return function(val){
        return args.reduce((a, b) => b(a), val)
    }
}
let a1 = (a) => a + a;
let a2 = (a) => a * a;
console.log(compose(a1, a2)(10))


function debounce(fn, wait){
    let timer = null
    return (...args) => {
        clearTimeout(timer)
        timer = setTimeout(() => {
            fn.call(this, ...args)
        }, wait)
    }

}

function betterDebounce(fn,wait, inmediate){
    let timer = null
    return (...args) => {
        clearTimeout(timer)
        if(inmediate && !timer){
            timer = setTimeout(() => {
                timer = null
            }, wait)
            fn.call(this, ...args)
        } else {
            timer = setTimeout(() => {
                fn.call(this, ...args)
                timer = null
            }, wait)
        }

    }
}


function throttle(fn, wait){
    let timer = null;
    return (...args) => {
        if(timer) return;
        timer = setTimeout(() => {
            fn.call(this, ...args)
            timer = null
        }, wait)
    }
}


function betterThrottle(){
    let timer = null;
    let start = Date.now()
    return (...args) => {
        let currtime = Date.now();
        let time = wait - (currtime - start);
        if(time < 0){
            if(timer){
                clearTimeout(timer)
                timer = null
            }
            fn.call(this, ...args)
            start = Date.now()

        } else {
            if(timer) return;
            timer = setTimeout(() => {
                fn.call(this, ...args)
                timer = null
            }, wait)
        }
    }
}

